﻿using System;
using System.Collections.Generic;
using System.Linq;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using UnityEngine;

namespace JH
{
    public partial class AroundWithMoveCameraConfig
    {
        /// <summary>
        /// 屏幕空间垂直方向上的移动映射到什么方向
        /// </summary>
        public enum ScreenVerticalMapping
        {
            /// <summary>
            ///  相机空间的前方
            /// </summary>
            CameraForward,

            /// <summary>
            ///  相机空间的上方
            /// </summary>
            CameraUp,
        }

        /// <summary>
        ///  移动配置
        /// </summary>
        [Serializable]
        public class MoveConfig
        {
            /// <summary>
            ///  是否允许移动
            /// </summary>
#if ODIN_INSPECTOR
            [LabelText("是否允许移动")]
#endif
            public bool allowMove = true;

            /// <summary>
            ///  屏幕空间垂直方向上的移动映射到什么方向
            /// </summary>
#if ODIN_INSPECTOR
            [LabelText("屏幕空间垂直方向上的移动映射到什么方向")]  [ShowIf("allowMove")]
            [EnumToggleButtons]
#endif
            public ScreenVerticalMapping mapping = ScreenVerticalMapping.CameraForward;

            /// <summary>
            ///  最小半径时的移动速度
            /// </summary>
#if ODIN_INSPECTOR
            [ShowIf("allowMove")] [LabelText("最小半径时的移动速度")]
#endif
            public Vector2 minRadiusSpeed = new(0.5f, 0.5f);

            /// <summary>
            ///  最大半径时的移动速度
            /// </summary>
#if ODIN_INSPECTOR
            [ShowIf("allowMove")] [LabelText("最大半径时的移动速度")]
#endif
            public Vector2 maxRadiusSpeed = new(16f, 9f);

            /// <summary>
            ///  归一化半径-移动速度曲线
            /// </summary>
#if ODIN_INSPECTOR
            [ShowIf("allowMove")] [LabelText("归一化半径-移动速度曲线")]
#endif
            public AnimationCurve moveSpeedRadiusCurve = AnimationCurve.Linear(0, 1, 1, 1);

            /// <summary>
            ///  获取移动速度
            /// </summary>
            /// <param name="normalizedRadius">当前归一化半径</param>
            public Vector2 GetMoveSpeed(float normalizedRadius)
            {
                var normalizedSpeed = moveSpeedRadiusCurve.Evaluate(normalizedRadius);
                return Vector2.Lerp(minRadiusSpeed, maxRadiusSpeed, normalizedSpeed);
            }

            /// <summary>
            ///  移动阻尼
            /// </summary>
#if ODIN_INSPECTOR
            [ShowIf("allowMove")] [LabelText("移动阻尼")]
#endif
            public float moveDamping = 3;

            /// <summary>
            ///  是否有限制
            /// </summary>
            public bool NeedLimit => limits is { Count: > 0 };

            /// <summary>
            ///  世界空间下的最小限制位置
            /// </summary>
            public Vector3 MinPosition => GetLimitsBounds().min;

            /// <summary>
            ///  世界空间下的最大限制位置
            /// </summary>
            public Vector3 MaxPosition => GetLimitsBounds().max;

            /// <summary>
            ///  看向点移动限制
            /// </summary>
#if ODIN_INSPECTOR
            [ShowIf("allowMove")] [LabelText("移动限制")]
#endif
            public List<GameObject> limits = new();

            /// <summary>
            ///  获取给定向量在限制平面的投影，归一化
            /// </summary>
            public Vector3 GetVectorProjection(Vector3 vector)
            {
                if (!NeedLimit) return vector;
                // 获取平面的法向量
                var planeNormal = GetLimitPlane().normal;

                // 确保法向量是单位向量（归一化）
                planeNormal.Normalize();

                // 计算向量在平面上的投影
                var projection = vector - Vector3.Dot(vector, planeNormal) * planeNormal;

                return projection.normalized;
            }

            /// <summary>
            ///  获取限制的平面
            /// </summary>
            public Plane GetLimitPlane()
            {
                return new Plane(Vector3.up, GetLimitsBounds().min);
            }

            /// <summary>
            ///  获取限制的边界
            /// </summary>
            private (Vector3 min, Vector3 max) GetLimitsBounds()
            {
                //计算坐标的最小值和最大值
                var positions = limits
                    .Where(t => t)
                    .Select(t => t.GetComponent<Transform>().position)
                    .ToHashSet();
                var xList = positions.Select(t => t.x).ToList();
                var yList = positions.Select(t => t.y).ToList();
                var zList = positions.Select(t => t.z).ToList();
                var min = new Vector3(xList.Min(), yList.Min(), zList.Min());
                var max = new Vector3(xList.Max(), yList.Max(), zList.Max());

                //获取渲染器，计算bounds
                var renders = limits
                    .Where(t => t).Distinct()
                    .Select(t => t.GetComponent<Renderer>())
                    .ToHashSet();

                //如果没有渲染器，直接返回坐标的限制
                if (renders.Count == 0) return (min, max);

                // 计算边界
                var first = renders.First();
                var bounds = first.bounds;
                for (int i = 1; i < renders.Count; i++)
                {
                    var eachRender = renders.ElementAt(i);
                    if (!eachRender) continue;
                    bounds.Encapsulate(eachRender.bounds);
                }

                //获取边界的最小值和最大值
                var boundsMin = bounds.min;
                var boundsMax = bounds.max;

                //组合坐标和边界的最小值和最大值
                return (new Vector3(Mathf.Min(min.x, boundsMin.x),
                        Mathf.Min(min.y, boundsMin.y),
                        Mathf.Min(min.z, boundsMin.z)),
                    new Vector3(Mathf.Max(max.x, boundsMax.x),
                        Mathf.Max(max.y, boundsMax.y),
                        Mathf.Max(max.z, boundsMax.z)));
            }
        }
    }
}